/*HEADER**********************************************************************
*
* Copyright 2008 Freescale Semiconductor, Inc.
* Copyright 1989-2008 ARC International
*
* This software is owned or controlled by Freescale Semiconductor.
* Use of this software is governed by the Freescale MQX RTOS License
* distributed with this Material.
* See the MQX_RTOS_LICENSE file distributed for more details.
*
* Brief License Summary:
* This software is provided in source form for you to use free of charge,
* but it is not open source software. You are allowed to use this software
* but you cannot redistribute it or derivative works of it in source form.
* The software may be used only in connection with a product containing
* a Freescale microprocessor, microcontroller, or digital signal processor.
* See license agreement file for full license terms including other
* restrictions.
*****************************************************************************
*
* Comments:
*
*   This file Contains the implementation of class driver for CDC devices.
*
*
*END************************************************************************/

#include "usb_host_serial.h"

#if MQX_USE_IO_OLD //TODO: make CDC independent on MQX_FILE_PTR (old IO subsystem)

//#define _HOST_DEBUG_

static void usb_class_serial_int_callback(_usb_pipe_handle, void *, void *, uint32_t, uint32_t);
static void usb_class_serial_in_callback(_usb_pipe_handle, void *, void *, uint32_t, uint32_t);
static void usb_class_serial_out_callback(_usb_pipe_handle, void *, void *, uint32_t, uint32_t);

static _mqx_int _io_usb_serial_char_avail(MQX_FILE_PTR fd_ptr);
static _mqx_int _io_usb_serial_open(MQX_FILE_PTR, char *, char *);
static _mqx_int _io_usb_serial_close(MQX_FILE_PTR);
static _mqx_int _io_usb_serial_read(MQX_FILE_PTR, char *, _mqx_int);
static _mqx_int _io_usb_serial_write(MQX_FILE_PTR, char *, _mqx_int);
static _mqx_int _io_usb_serial_ioctl(MQX_FILE_PTR, _mqx_uint, void *);
static _mqx_int _io_usb_serial_uninstall(IO_DEVICE_STRUCT_PTR);


/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_class_serial_cntrl_callback
* Returned Value : USB_OK if command has been passed on USB.
* Comments       :
*     This is the callback used when audio control information is sent or received.
*
*END*--------------------------------------------------------------------*/

static void usb_class_serial_cntrl_callback
(
    /* [IN] Unused */
    void       *pipe,

    /* [IN] Pointer to the class interface instance */
    void       *param,

    /* [IN] Data buffer */
    unsigned char   *buffer,

    /* [IN] Length of buffer */
    uint32_t     len,

    /* [IN] Error code (if any) */
    USB_STATUS  status
)
{ /* Body */
} /* Endbody */


/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_class_serial_int_callback
* Returned Value : None
* Comments       :
*     Called on interrupt endpoint data reception.
*
*END*--------------------------------------------------------------------*/

static void usb_class_serial_int_callback
   (
      /* [IN] pointer to pipe */
      _usb_pipe_handle  pipe,

      /* [IN] user-defined parameter */
      void             *param,

      /* [IN] buffer address */
      void             *buffer,

      /* [IN] length of data transferred */
      uint32_t           len,

      /* [IN] status, hopefully USB_OK or USB_DONE */
      uint32_t           status
   )
{ /* Body */
//    CLASS_CALL_STRUCT_PTR         acm_instance = (CLASS_CALL_STRUCT_PTR) param;
//    USB_ACM_CLASS_INTF_STRUCT_PTR if_ptr;

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_int_callback");
    #endif

//TODO:    if_ptr = (USB_ACM_CLASS_INTF_STRUCT_PTR) acm_instance->class_intf_handle;

    /* we do not use USB_ACM_INT_PIPE_FREE in this version at all */
//TODO:    _lwevent_set(if_ptr->acm_event, USB_ACM_INT_PIPE_FREE);

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_int_callback, SUCCESSFUL");
    #endif

} /*EndBody */

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_class_serial_in_callback
* Returned Value : Success as USB_OK
* Comments       :
*     Called on interrupt endpoint data reception
*END*--------------------------------------------------------------------*/

static void usb_class_serial_in_callback
   (
      /* [IN] pointer to pipe */
      _usb_pipe_handle  pipe,

      /* [IN] user-defined parameter */
      void             *param,

      /* [IN] buffer address */
      void             *buffer,

      /* [IN] length of data transferred */
      uint32_t           len,

      /* [IN] status, hopefully USB_OK or USB_DONE */
      uint32_t           status
   )
{ /* Body */
    CLASS_CALL_STRUCT_PTR          serial_instance = (CLASS_CALL_STRUCT_PTR) param;
    USB_SERIAL_CLASS_INTF_STRUCT_PTR if_ptr;

    
    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_in_callback");
    #endif
    
    if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

    if ((serial_instance == NULL) || !usb_host_class_intf_validate(serial_instance))
        len = 0;

    /* in the case we have less data than expected, status is not USB_OK, but buffer is not NULL */
    if ((status != USB_OK) && (buffer == NULL)) /* if no data received */
        len = 0;

    if (if_ptr->RX_BUFFER_DRV != NULL)
        if_ptr->RX_BUFFER_DRV += len;
    if_ptr->RX_READ = len;

    if (if_ptr->serial_event != NULL)
        _lwevent_set(if_ptr->serial_event, USB_SERIAL_READ_COMPLETE); /* signal that we have completed transfer on input pipe */

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_in_callback, SUCCESSFUL");
    #endif

} /*EndBody */

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_class_serial_out_callback
* Returned Value : Success as USB_OK
* Comments       :
*     Called on interrupt endpoint data reception
*END*--------------------------------------------------------------------*/

static void usb_class_serial_out_callback
   (
      /* [IN] pointer to pipe */
      _usb_pipe_handle  pipe,

      /* [IN] user-defined parameter */
      void             *param,

      /* [IN] buffer address */
      void             *buffer,

      /* [IN] length of data transferred */
      uint32_t           len,

      /* [IN] status, hopefully USB_OK or USB_DONE */
      uint32_t           status
   )
{ /* Body */
    CLASS_CALL_STRUCT_PTR          serial_instance = (CLASS_CALL_STRUCT_PTR) param;
    USB_SERIAL_CLASS_INTF_STRUCT_PTR if_ptr;

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_out_callback");
    #endif

    if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

    /* in the case we have less data than expected, status is not USB_OK, but buffer is not NULL */
    if ((status != USB_OK) && (buffer == NULL)) /* if no data received */
        len = 0;

    if_ptr->TX_SENT = len;

    if (if_ptr->serial_event != NULL)
        _lwevent_set(if_ptr->serial_event, USB_SERIAL_SEND_COMPLETE); /* signal that we have completed transfer on output pipe */

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_out_callback, SUCCESSFUL");
    #endif

} /*EndBody */

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_class_serial_init_ipipe
* Returned Value : Success as USB_OK, no interrupt pipe (not error!) as
* Comments       :
*     Starts interrupt endpoint to poll for interrupt on specified device.
*END*--------------------------------------------------------------------*/

USB_STATUS usb_class_serial_init_ipipe
    (
        /* serial interface instance */
        CLASS_CALL_STRUCT_PTR      serial_instance
    )
{ /* Body */
    TR_INIT_PARAM_STRUCT           tr;
    USB_STATUS                     status = USB_OK;
    USB_SERIAL_CLASS_INTF_STRUCT_PTR  if_ptr;

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_init_ipipe");
    #endif
#if 1
    USB_lock();
    /* Validity checking, always needed when passing data to lower API */
    if (usb_host_class_intf_validate(serial_instance)) {
        if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

        if (if_ptr->int_in_pipe != NULL) {
            usb_hostdev_tr_init(&tr, (tr_callback) usb_class_serial_int_callback, (void *) serial_instance);
            tr.G.RX_BUFFER = (unsigned char *) &if_ptr->INT_IN_BUFFER;
            tr.G.RX_LENGTH = sizeof(if_ptr->INT_IN_BUFFER);

            if (USB_STATUS_TRANSFER_QUEUED == (status = _usb_host_recv_data(
                if_ptr->SERIAL_G.G.host_handle,
                if_ptr->int_in_pipe,
                &tr
            )))
            {
                status = USB_OK;
            }
        }
//        else
//            status = USBERR_OPEN_PIPE_FAILED;
    }

    USB_unlock();
#endif
    #ifdef _HOST_DEBUG_
    if (!status) {
         DEBUG_LOG_TRACE("usb_class_serial_init_ipipe, SUCCESSFUL");
    }
    else {
         DEBUG_LOG_TRACE("usb_class_serial_init_ipipe, FAILED");
    }
    #endif

    return status;
}

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_class_serial_install_driver
* Returned Value : Success as USB_OK
* Comments       :
*     Adds / installs USB serial device driver to the MQX drivers
*END*--------------------------------------------------------------------*/

USB_STATUS usb_class_serial_install_driver
    (
        CLASS_CALL_STRUCT_PTR    serial_instance,
        char                 *device_name
    )
{
    USB_SERIAL_CLASS_INTF_STRUCT_PTR  if_ptr;
    USB_STATUS      status = USBERR_NO_INTERFACE;

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_serial_install_driver");
    #endif

    USB_lock();
    /* Validity checking, always needed when passing data to lower API */
    if (usb_host_class_intf_validate(serial_instance)) {
        if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

        status = _io_dev_install_ext(device_name,
            _io_usb_serial_open,
            _io_usb_serial_close,
            _io_usb_serial_read,
            _io_usb_serial_write,
            _io_usb_serial_ioctl,
            _io_usb_serial_uninstall,
            serial_instance); /* pass a parameter to the driver to associate this driver with data instance */
        if (status == IO_OK)
            if_ptr->device_name = device_name;
    }

    USB_unlock();

    #ifdef _HOST_DEBUG_
    if (!status) {
        DEBUG_LOG_TRACE("usb_class_serial_install_driver, SUCCESSFUL");
    }
    else {
        DEBUG_LOG_TRACE("usb_class_serial_install_driver, FAILED");
    }
    #endif

    return status;
}

/*FUNCTION*----------------------------------------------------------------
*
* Function Name  : usb_class_serial_uninstall_driver
* Returned Value : Success as USB_OK
* Comments       :
*     Removes USB serial device driver from the MQX drivers
*END*--------------------------------------------------------------------*/

USB_STATUS usb_class_serial_uninstall_driver
    (
        CLASS_CALL_STRUCT_PTR serial_instance
    )
{
    USB_SERIAL_CLASS_INTF_STRUCT_PTR  if_ptr;
    USB_STATUS      status = USBERR_NO_INTERFACE;

    #ifdef _HOST_DEBUG_
        DEBUG_LOG_TRACE("usb_class_cdc_uninstall_driver");
    #endif

    USB_lock();
    /* Validity checking, always needed when passing data to lower API */
    if (usb_host_class_intf_validate(serial_instance)) {
        if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

        status = _io_dev_uninstall(if_ptr->device_name);
    }

    USB_unlock();

    #ifdef _HOST_DEBUG_
    if (!status) {
        DEBUG_LOG_TRACE("usb_class_acm_uninstall_driver, SUCCESSFUL");
    }
    else {
        DEBUG_LOG_TRACE("usb_class_acm_uninstall_driver, FAILED");
    }
    #endif

    return status;
}

/*FUNCTION****************************************************************
*
* Function Name    : _io_usb_serial_open
* Returned Value   : _mqx_int error code
* Comments         :
*    This routine initializes a USB serial I/O channel. It acquires
*    memory, then stores information into it about the channel.
*    It then calls the hardware interface function to initialize the channel.
*
*END**********************************************************************/

static _mqx_int _io_usb_serial_open
   (
      /* [IN] the file handle for the device being opened */
      MQX_FILE_PTR fd_ptr,

      /* [IN] the remaining portion of the name of the device */
      char        *open_name_ptr,

      /* [IN] the flags to be used during operation:
      ** echo, translation, xon/xoff, encoded into a pointer.
      */
      char        *flags
   )
{ /* Body */
#if 1
    CLASS_CALL_STRUCT_PTR           serial_instance;
    USB_SERIAL_CLASS_INTF_STRUCT_PTR if_ptr;
    USB_SETUP                        req;
    USB_STATUS                       status = USBERR_NO_INTERFACE;
    int val = 0;

    serial_instance = (CLASS_CALL_STRUCT_PTR) fd_ptr->DEV_PTR->DRIVER_INIT_PTR;
    
    USB_lock();
    /* Validity checking, always needed when passing data to lower API */
    if (!usb_host_class_intf_validate(serial_instance)) {
        USB_unlock();
        fd_ptr->ERROR = USBERR_NO_INTERFACE;
        return IO_ERROR;
    }
    if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

    if (if_ptr->dtr_state)
        val |= 0x01;
	if (if_ptr->rts_state)
        val |= 0x02;

    /* Setup the request */
    req.BMREQUESTTYPE   = 0x21;
    req.BREQUEST        = 0x22;

    *(uint16_t*)req.WINDEX  = HOST_TO_LE_SHORT(if_ptr->SERIAL_G.IFNUM);
    *(uint16_t*)req.WVALUE  = HOST_TO_LE_SHORT(val);
    *(uint16_t*)req.WLENGTH = HOST_TO_LE_SHORT(0);

    status = _usb_hostdev_cntrl_request(if_ptr->SERIAL_G.G.dev_handle,
                                        &req, NULL,
                                       NULL,
                                       NULL);

    printf("IFNUM =%d\r\n", if_ptr->SERIAL_G.IFNUM);

                                       
    if ( USB_STATUS_TRANSFER_QUEUED == status)
    {
        printf("_io_usb_serial_open ok!\r\n");
        return IO_OK;
    }

    printf("_io_usb_serial_open error!\r\n");
    return IO_ERROR;
#else
    return IO_OK;
#endif
} /* Endbody */


/*FUNCTION****************************************************************
*
* Function Name    : _io_usb_serial_close
* Returned Value   : _mqx_int error code
* Comments         :
*    This routine closes the USB serial I/O channel.
*
*END**********************************************************************/

static _mqx_int _io_usb_serial_close
   (
      /* [IN] the file handle for the device being closed */
      MQX_FILE_PTR fd_ptr
   )
{ /* Body */
    return IO_OK;
}

/*FUNCTION****************************************************************
*
* Function Name    : _io_usb_serial_read
* Returned Value   : _mqx_int number of characters read
* Comments         :
*    This routine reads characters from the USB serial I/O channel
*    device, converting carriage return ('\r') characters to newlines,
*    and then echoing the input characters.
*
*END*********************************************************************/

static _mqx_int _io_usb_serial_read
   (
      /* [IN] the file handle for the device */
      MQX_FILE_PTR fd_ptr,

      /* [IN] where the characters are to be stored */
      char   *data_ptr,

      /* [IN] the number of characters to input */
      _mqx_int   num
   )
{ /* Body */
    CLASS_CALL_STRUCT_PTR          serial_instance;
    USB_SERIAL_CLASS_INTF_STRUCT_PTR if_ptr;
    TR_INIT_PARAM_STRUCT           tr;
    USB_STATUS                     status;
    _mqx_int                       num_left, block_len;
    LWEVENT_STRUCT_PTR             event;

    if (0 == (num_left = num))
       return 0;

//    printf("_io_usb_serial_read---->\n");

    serial_instance = (CLASS_CALL_STRUCT_PTR) fd_ptr->DEV_PTR->DRIVER_INIT_PTR;

    USB_lock();
    /* Validity checking, always needed when passing data to lower API */
    if (!usb_host_class_intf_validate(serial_instance)) {
        USB_unlock();
        fd_ptr->ERROR = USBERR_NO_INTERFACE;
        printf("[%s] return @ %d line\n", __FUNCTION__,__LINE__); 
        return IO_ERROR;
    }
    if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

    /* Does the device use IN pipe? */
    if (if_ptr->in_pipe == NULL) {
        USB_unlock();
        fd_ptr->ERROR = USBERR_OPEN_PIPE_FAILED;
        printf("[%s] return @ %d line\n", __FUNCTION__,__LINE__); 
        return IO_ERROR;
    }

    block_len = if_ptr->RX_BUFFER_SIZE;
    event = if_ptr->serial_event;

    /* If we have USB_UART_NO_BLOCKING, we want to be sure that we will not be waiting for the pipe.
    ** We just check if the pipe is busy (event USB_DATA_READ_PIPE_FREE is not set). If it is, we should return,
    ** There is no other way to check the status of event without waiting than to access its structure.
    ** Known issue:
    ** This approach takes the risk that the 2 tasks access USB_UART_NO_BLOCKING file, then both can read "free pipe"
    ** and next on waiting for USB_DATA_READ_PIPE_FREE one of them takes the event and the other one waits blocked
    ** until the pipe is free (and waiting is not accepted with USB_UART_NO_BLOCKING).
    */
    if ((((USB_SERIAL_INIT_PTR) fd_ptr->FLAGS)->FLAGS & USB_UART_NO_BLOCKING) && !(event->VALUE & USB_SERIAL_READ_PIPE_FREE)) {
        USB_unlock();
        printf("[%s] return @ %d line\n", __FUNCTION__,__LINE__); 
        return 0;
    }


    if (event != NULL) {
        USB_unlock();
        _lwevent_wait_ticks(event, USB_SERIAL_READ_PIPE_FREE | USB_SERIAL_DETACH, FALSE, 0);
        USB_lock();
    }

    /* Validity checking, always needed when passing data to lower API */
    if (!usb_host_class_intf_validate(serial_instance)) {
        USB_unlock();
        fd_ptr->ERROR = USBERR_NO_INTERFACE;
        printf("[%s] return @ %d line\n", __FUNCTION__,__LINE__); 
        return IO_ERROR;
    }

//    printf("total =%d\n", num_left);

    while (if_ptr->RX_BUFFER_APP < if_ptr->RX_BUFFER_DRV)
    {
        *data_ptr++ = *if_ptr->RX_BUFFER_APP++;
        num_left--;
        if (!num_left)
            break;
    }

//    printf("num_left =%d\n", num_left);

    while (num_left)
    {
        usb_hostdev_tr_init(&tr, (tr_callback) usb_class_serial_in_callback, (void *) serial_instance);

        if_ptr->RX_BUFFER_APP = if_ptr->RX_BUFFER;
        if_ptr->RX_BUFFER_DRV = if_ptr->RX_BUFFER;
        tr.G.RX_BUFFER = (unsigned char *) if_ptr->RX_BUFFER;
        tr.G.RX_LENGTH = 64;

        if (event != NULL) {
            _lwevent_clear(event, USB_SERIAL_READ_COMPLETE);
        }
        status = _usb_host_recv_data(if_ptr->SERIAL_G.G.host_handle, if_ptr->in_pipe, &tr);
        if (status == USB_STATUS_TRANSFER_QUEUED) {

            if (event != NULL) {
                USB_unlock();
                /* serialize requests by waiting for pipe */
                /* wait while data will be available */
                _lwevent_wait_ticks(event, USB_SERIAL_READ_COMPLETE | USB_SERIAL_DETACH, FALSE, 0);

                USB_lock();
            }

            /* Validity checking, always needed when passing data to lower API */
            if (!usb_host_class_intf_validate(serial_instance)) {
                USB_unlock();
                fd_ptr->ERROR = USBERR_NO_INTERFACE;
                printf("[%s] return @ %d line\n", __FUNCTION__,__LINE__); 
                return IO_ERROR;
            }

            if (tr.G.RX_BUFFER != (unsigned char *) data_ptr) {
                while ((if_ptr->RX_BUFFER_APP < if_ptr->RX_BUFFER_DRV) && num_left) {
                    *data_ptr++ = *if_ptr->RX_BUFFER_APP++;
                    num_left--;
                }
            }
            else {
                data_ptr += if_ptr->RX_READ;
                num_left -= if_ptr->RX_READ;
            }

            if (num_left && !(((USB_SERIAL_INIT_PTR) fd_ptr->FLAGS)->FLAGS & USB_UART_NO_BLOCKING)) {
                if (event != NULL) {
                    /* wait 10 ticks to next read */
                    USB_unlock();
                    _lwevent_wait_ticks(event, USB_SERIAL_DETACH, FALSE, 10);
                    USB_lock();
                }
                /* Validity checking, always needed when passing data to lower API */
                if (!usb_host_class_intf_validate(serial_instance)) {
                    USB_unlock();
                    fd_ptr->ERROR = USBERR_NO_INTERFACE;
                    printf("[%s] return @ %d line\n", __FUNCTION__,__LINE__); 
                    return IO_ERROR;
                }
            }
            else
                break;
        }
        else {
            break;
        }
    }

    if (event != NULL) {
        _lwevent_set(event, USB_SERIAL_READ_PIPE_FREE); /* mark we are not using input pipe */
    }

    USB_unlock();
//    printf("[%s] return @ %d line\n", __FUNCTION__,__LINE__); 
    return num - num_left;

} /* Endbody */

/*FUNCTION****************************************************************
*
* Function Name    : _io_usb_serial_char_avail
* Returned Value   : TRUE if char is available, FALSE if not
* Comments         :
*    This routine reads characters from the USB serial I/O channel
*    device, converting carriage return ('\r') characters to newlines,
*    and then echoing the input characters.
*
*END*********************************************************************/

static _mqx_int _io_usb_serial_char_avail
   (
      /* [IN] the file handle for the device */
      MQX_FILE_PTR fd_ptr
   )
{ /* Body */
    CLASS_CALL_STRUCT_PTR          serial_instance;
    USB_SERIAL_CLASS_INTF_STRUCT_PTR if_ptr;
    TR_INIT_PARAM_STRUCT           tr;
    USB_STATUS                     status;
    LWEVENT_STRUCT_PTR             event;

    serial_instance = (CLASS_CALL_STRUCT_PTR) fd_ptr->DEV_PTR->DRIVER_INIT_PTR;

    USB_lock();
    /* Validity checking, always needed when passing data to lower API */
    if (!usb_host_class_intf_validate(serial_instance)) {
        USB_unlock();
        return USBERR_NO_INTERFACE;
    }
    if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

    if (if_ptr->in_pipe == NULL) {
        USB_unlock();
        fd_ptr->ERROR = USBERR_OPEN_PIPE_FAILED;
        return IO_ERROR;
    }

    event = if_ptr->serial_event;

    if ((event != NULL) && !(event->VALUE & USB_SERIAL_READ_PIPE_FREE)) {
        /* any other task is reading => no character available for us now */
        USB_unlock();
        return FALSE;
    }
    if (if_ptr->RX_BUFFER_APP != if_ptr->RX_BUFFER_DRV) {
        /* cache is not empty => char is available */
        USB_unlock();
        return TRUE;
    }

    if (event != NULL) {
        /* following command never waits as USB_DATA_READ_PIPE_FREE is set (tested above) */
        _lwevent_wait_ticks(event, USB_SERIAL_READ_PIPE_FREE, FALSE, 0);
    }

    /* We are going to try reading data. We will see if some data is available or not. */

    usb_hostdev_tr_init(&tr, (tr_callback) usb_class_serial_in_callback, (void *) serial_instance);
    if_ptr->RX_BUFFER_APP = if_ptr->RX_BUFFER;
    if_ptr->RX_BUFFER_DRV = if_ptr->RX_BUFFER;
    tr.G.RX_BUFFER = (unsigned char *) if_ptr->RX_BUFFER;
    tr.G.RX_LENGTH = if_ptr->RX_BUFFER_SIZE;

    if (event != NULL) {
        _lwevent_clear(event, USB_SERIAL_READ_COMPLETE);
    }
    status = _usb_host_recv_data(if_ptr->SERIAL_G.G.host_handle, if_ptr->in_pipe, &tr);

    if (status == USB_STATUS_TRANSFER_QUEUED) {
        if (event != NULL) {
            USB_unlock();
            /* serialize requests by waiting for pipe */
            /* wait while data will be available */
            _lwevent_wait_ticks(event, USB_SERIAL_READ_COMPLETE | USB_SERIAL_DETACH, FALSE, 0);

            USB_lock();
        }
        /* Validity checking, always needed when passing data to lower API */
        if (!usb_host_class_intf_validate(serial_instance)) {
            USB_unlock();
            return FALSE;
        }

        status = (if_ptr->RX_READ != 0 ? TRUE : FALSE);
    }
    else
        status = FALSE;

    if (event != NULL) {
        _lwevent_set(event, USB_SERIAL_READ_PIPE_FREE); /* mark we are not using input pipe */
    }

    USB_unlock();

    return status;

} /* Endbody */

/*FUNCTION****************************************************************
*
* Function Name    : _io_usb_serial_write
* Returned Value   : _mqx_int number of characters written
* Comments         :
*    This routine writes characters to the USB serial I/O channel.
*
*END**********************************************************************/

static _mqx_int _io_usb_serial_write
   (
      /* [IN] the file handle for the device */
      MQX_FILE_PTR fd_ptr,

      /* [IN] where the characters are */
      char  *data_ptr,

      /* [IN] the number of characters to output */
      _mqx_int  num
   )
{ /* Body */
    CLASS_CALL_STRUCT_PTR          serial_instance;
    USB_SERIAL_CLASS_INTF_STRUCT_PTR if_ptr;
    TR_INIT_PARAM_STRUCT           tr;
    USB_STATUS                     status = USBERR_NO_INTERFACE;
    uint32_t                       num_left;
    LWEVENT_STRUCT_PTR             event;

    {
        int i;
        for (i=0; i<num; i++)
            printf("%02x ", data_ptr[i]);

        printf("\n");
    }


    if (0 == (num_left = num))
       return 0;

    serial_instance = (CLASS_CALL_STRUCT_PTR) fd_ptr->DEV_PTR->DRIVER_INIT_PTR;

    USB_lock();
    /* Validity checking, always needed when passing data to lower API */
    if (!usb_host_class_intf_validate(serial_instance)) {
        USB_unlock();
        fd_ptr->ERROR = USBERR_NO_INTERFACE;
        return IO_ERROR;
    }
    if_ptr = (USB_SERIAL_CLASS_INTF_STRUCT_PTR) serial_instance->class_intf_handle;

    /* Do the device use OUT pipe? */
    if (if_ptr->out_pipe == NULL) {
        USB_unlock();
        return USBERR_OPEN_PIPE_FAILED;
    }

    event = if_ptr->serial_event;

    if ((((USB_SERIAL_INIT_PTR) fd_ptr->FLAGS)->FLAGS & USB_UART_NO_BLOCKING) && !(event->VALUE & USB_SERIAL_SEND_PIPE_FREE)) {
        USB_unlock();
        return 0;
    }

    USB_unlock();

    _lwevent_wait_ticks(event, USB_SERIAL_SEND_PIPE_FREE | USB_SERIAL_DETACH, FALSE, 0);
    USB_lock();

    /* Validity checking, always needed when passing data to lower API */
    if (!usb_host_class_intf_validate(serial_instance)) {
        USB_unlock();
        fd_ptr->ERROR = USBERR_NO_INTERFACE;
        return IO_ERROR;
    }

    while (num_left) {
        usb_hostdev_tr_init(&tr, (tr_callback) usb_class_serial_out_callback, (void *) serial_instance);
        tr.G.TX_BUFFER = (unsigned char *) data_ptr;
        tr.G.TX_LENGTH = num_left;

        _lwevent_clear(event, USB_SERIAL_SEND_COMPLETE);
        status = _usb_host_send_data(if_ptr->SERIAL_G.G.host_handle, if_ptr->out_pipe, &tr);
        if (status == USB_STATUS_TRANSFER_QUEUED) {

            USB_unlock();
            /* serialize requests by waiting for pipe */
            /* wait while data will be sent */
            _lwevent_wait_ticks(event, USB_SERIAL_SEND_COMPLETE | USB_SERIAL_DETACH, FALSE, 0);

            USB_lock();

            /* Validity checking, always needed when passing data to lower API */
            if (!usb_host_class_intf_validate(serial_instance)) {
                USB_unlock();
                fd_ptr->ERROR = USBERR_NO_INTERFACE;
                printf("return %d\n", __LINE__);
                return IO_ERROR;
            }

            data_ptr += if_ptr->TX_SENT;
            num_left -= if_ptr->TX_SENT;

            if (num_left && !(((USB_SERIAL_INIT_PTR) fd_ptr->FLAGS)->FLAGS & USB_UART_NO_BLOCKING)) {
                /* wait 10 ticks to next write */
                USB_unlock();
                _lwevent_wait_ticks(event, USB_SERIAL_DETACH, FALSE, 10);
                USB_lock();
                /* Validity checking, always needed when passing data to lower API */
                if (!usb_host_class_intf_validate(serial_instance)) {
                    USB_unlock();
                    fd_ptr->ERROR = USBERR_NO_INTERFACE;
                    printf("return %d\n", __LINE__);
                    return IO_ERROR;
                }
            }
            else
                break;
        }
        else {
            break;
        }
    }

    _lwevent_set(event, USB_SERIAL_SEND_PIPE_FREE); /* mark we are not using input pipe */

    USB_unlock();
    return num - num_left;
} /* Endbody */


/*FUNCTION*****************************************************************
*
* Function Name    : _io_usb_serial_ioctl
* Returned Value   : _mqx_int
* Comments         :
*    Returns result of ioctl operation.
*
*END*********************************************************************/

static _mqx_int _io_usb_serial_ioctl
   (
      /* [IN] the file handle for the device */
      MQX_FILE_PTR   fd_ptr,

      /* [IN] the ioctl command */
      _mqx_uint      cmd,

      /* [IN] the ioctl parameters */
      void          *param_ptr
   )
{ /* Body */

    _mqx_int      *bparam_ptr;
    uint32_t       *pparam_ptr;

    switch (cmd) {
        case IO_IOCTL_CHAR_AVAIL:
            bparam_ptr  = (_mqx_int *) param_ptr;
            fd_ptr->ERROR = _io_usb_serial_char_avail(fd_ptr);
            if ((fd_ptr->ERROR != (_mqx_int) TRUE) && (fd_ptr->ERROR != (_mqx_int) FALSE))
                return IO_ERROR;
            *bparam_ptr = fd_ptr->ERROR;
            break;

        case IO_IOCTL_SERIAL_GET_FLAGS:
            pparam_ptr  = (_mqx_uint_ptr) param_ptr;
            *pparam_ptr = fd_ptr->FLAGS;
            break;

        case IO_IOCTL_SERIAL_SET_FLAGS:
            pparam_ptr  = (_mqx_uint_ptr) param_ptr;
            fd_ptr->FLAGS = *pparam_ptr;
            break;
    }
    return IO_OK;
} /* Endbody */

/*FUNCTION*****************************************************************
*
* Function Name    : _io_usb_serial_uninstall
* Returned Value   : _mqx_int
* Comments         :
*    Does nothing. Must be implemented to allow device to be uninstalled.
*
*END*********************************************************************/

static _mqx_int _io_usb_serial_uninstall
   (
       IO_DEVICE_STRUCT_PTR dev
   )
{ /* Body */
    return IO_OK;
} /* Endbody */
#endif //MQX_USE_IO_OLD

/* EOF */
